package com.ibatis4J.framework.dal.client.support;

import com.ibatis4J.framework.dal.client.IPaginationDalClient;
import com.ibatis4J.framework.dal.dao.support.rowmapper.RowMapperFactory;
import com.ibatis4J.framework.dal.dao.support.sql.FreeMakerParser;
import com.ibatis4J.framework.dal.dialect.Dialect;
import com.ibatis4J.framework.dal.dialect.DialectFactory;
import com.ibatis4J.framework.dal.exception.DalException;
import com.ibatis4J.framework.dal.pagination.Pagination;
import com.ibatis4J.framework.dal.pagination.PaginationResult;
import com.ibatis4J.framework.dal.resource.parse.SqlBean;
import com.ibatis4J.framework.dal.util.DalUtils;
import com.sun.xml.internal.ws.api.model.ExceptionType;
import com.thoughtworks.xstream.core.BaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 *带分页的客户端
 *
 * @author liuwenya
 *
 */
public class PaginationDalClient extends DefaultDalClient implements IPaginationDalClient{

    /** 日志 */
    private static final Logger LOGGER = LoggerFactory.getLogger(PaginationDalClient.class);

    /** SQL失效时间 */
    private static final Long SQLTIMEOUT = 100L;

    /**
     * 数据库方言工厂
     */
    @Autowired
    private DialectFactory dialectFactory;

    /**
     * 查询并返回分页结果，pagesize为负数时，返回所有记录
     * @param sqlId SQLID
     * @param param 查询参数
     * @param pagination 分页
     * @return  分页查询结果
     */
    public PaginationResult<List<Map<String, Object>>> queryForList(String sqlId, Map<String, Object> paramMap, Pagination pagination) {
        SqlBean sqlBean=getSQL(sqlId);
        String sql=FreeMakerParser.process(paramMap,sqlBean.getContent(),sqlId);
        /** 装配分页信息 */
        if (paramMap == null) {
            paramMap = new HashMap<String, Object>();
        }
        /** 当pagesize为负数时 ,查询该表中的所有记录 */
        List<Map<String, Object>> list = null;
        if(pagination.getPagesize() < 0){
            list=queryForList(sqlId ,paramMap);
            pagination.setTotalRows(list.size());
        } else {
            paramMap.put(LIMIT, pagination.getPagesize());
            paramMap.put("_offset", pagination.getFirstRowIndex());
            this.configurePagination(getJdbcTemplate(), sql, paramMap, pagination, sqlBean.getDbType());
            String paginationSql= generatePaginationSql(sql, sqlBean.getDbType(), pagination);
            this.resetPaginationParams(paramMap, pagination, sqlBean.getDbType());
            logMessage("PaginationDalClient ====> queryForList", paginationSql, paramMap);
            long beginDate = System.currentTimeMillis();
            list = getJdbcTemplate().queryForList(paginationSql,paramMap);
            logMessage("PaginationDalClient ====> queryForList", paginationSql, paramMap, System.currentTimeMillis() - beginDate);
        }
        /** 执行分页查询 */
        return new PaginationResult<List<Map<String, Object>>>(list, pagination);
    }

    /**
     * 获取分页处理结果 重载方法
     *
     * @param sqlId SQLID
     * @param paramMap 查询参数
     * @param requiredType 需要操作的类型
     * @param pagination 分页
     * @param <T> 泛型对象
     * @return List类型的查询结果
     */
    public <T> PaginationResult<List<T>> queryForList(String sqlId, Map<String, Object> paramMap, Class<T> requiredType, Pagination pagination) {
        return queryForList(sqlId,paramMap,new RowMapperFactory<T>(requiredType).getRowMapper(),pagination);
    }

    /**
     * 获取分页处理结果 重载方法
     *
     * @param sqlId SQLID
     * @param paramMap 查询参数
     * @param rowMapper 翻页处理规则
     * @param pagination 分页
     * @param <T> 泛型对象
     * @return List类型的查询结果
     */
    public <T> PaginationResult<List<T>> queryForList(String sqlId, Map<String, Object> paramMap, RowMapper<T> rowMapper,
                                               Pagination pagination){
        SqlBean sqlBean=getSQL(sqlId);
        String sql=FreeMakerParser.process(paramMap,sqlBean.getContent(),sqlId);
        /** 装配分页信息 */
        if (paramMap == null) {
            paramMap = new HashMap<String, Object>();
        }
        /** 当pagesize为负数时 ,查询该表中的所有记录 */
        List<T> list = null;
        if(pagination.getPagesize() < 0){
            list=queryForList(sqlId ,paramMap,rowMapper);
            pagination.setTotalRows(list.size());
        } else {
            paramMap.put(LIMIT, pagination.getPagesize());
            paramMap.put("_offset", pagination.getFirstRowIndex());
            this.configurePagination(getJdbcTemplate(), sql, paramMap, pagination, sqlBean.getDbType());
            String paginationSql= generatePaginationSql(sql, sqlBean.getDbType(), pagination);
            this.resetPaginationParams(paramMap, pagination, sqlBean.getDbType());
            logMessage("PaginationDalClient ====> queryForList", paginationSql, paramMap);
            long beginDate = System.currentTimeMillis();
            list = getJdbcTemplate().query(paginationSql,paramMap,rowMapper);
            logMessage("PaginationDalClient ====> queryForList", paginationSql, paramMap, System.currentTimeMillis() - beginDate);
        }
        /** 执行分页查询 */
        return new PaginationResult<List<T>>(list, pagination);
    }

    /**
     * 获取分页处理结果
     *
     * @param sqlId SQLID
     * @param param 查询参数
     * @param pagination 分页
     * @return List类型的查询结果
     */
    public PaginationResult<List<Map<String, Object>>> queryForList(String sqlId, Object param, Pagination pagination) {
        return queryForList(sqlId,DalUtils.convertToMap(param),pagination);
    }

    /**
     * 获取分页处理结果 重载方法
     *
     * @param sqlId SQLID
     * @param param 查询参数
     * @param rowMapper 翻页处理规则
     * @param pagination 分页
     * @param <T> 泛型对象
     * @return List类型的查询结果
     */
    public <T> PaginationResult<List<T>> queryForList(String sqlId, Object param, RowMapper<T> rowMapper, Pagination pagination) {
        return queryForList(sqlId,DalUtils.convertToMap(param),rowMapper,pagination);
    }

    /**
     * 获取分页处理结果 重载方法
     *
     * @param sqlId SQLID
     * @param param 查询参数
     * @param requiredType 需要操作的类型
     * @param pagination 分页
     * @param <T> 泛型对象
     * @return List类型的查询结果
     */
    public <T> PaginationResult<List<T>> queryForList(String sqlId, Object param, Class<T> requiredType, Pagination pagination) {
        return queryForList(sqlId,DalUtils.convertToMap(param),requiredType,pagination);
    }

    /**
     * 打印超时SQL的执行时间
     * @param method   方法名
     * @param sql      SQL串
     * @param object   对象
     * @param executeTime  执行时间
     */
    private void logMessage(String method, String sql, Object object, long executeTime) {

        if (executeTime >= SQLTIMEOUT) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.error(method + " method executeTime:" + executeTime + "ms");
            }
        }
    }

    /**
     * 打印sql的执行信息
     *
     * @param method 方法名
     * @param sql SQL串
     * @param object 对象
     */
    private void logMessage(String method, String sql, Object object) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(method + " method SQL: [" + sql + "]");
            LOGGER.debug(method + " method parameter:" + object);
        }
    }

    private Map<String, Object> resetPaginationParams(Map<String, Object> paramMap, Pagination pagination,
                                                      String dbType) {

        if ("db2".equals(dbType.toLowerCase())) {
            int db2Limit = pagination.getPagesize() * pagination.getCurrentPage();
            int db2Offset = pagination.getPagesize() * (pagination.getCurrentPage() - 1);
            paramMap.put(LIMIT, db2Limit);
            paramMap.put("_offset", db2Offset);
        }

        return paramMap;
    }

    /**
     * 生成分页sql，查询指定位置、指定行数的记录
     *
     * @param sql SQL语句
     * @param dbType 数据源类型
     * @param pagination 分页信息
     * @return 分页SQL串
     */
    private String generatePaginationSql(String sql, String dbType, Pagination pagination) {
        Dialect dialect = dialectFactory.getDBDialect(dbType);
        if (dialect == null) {
            throw new DalException("error.dal.005", "DB Dialect is not find.");
        }

        String paginationSql = dialect.getLimitString(sql);

        if ("db2".equals(dbType.toLowerCase())) {
            int db2Limit = pagination.getPagesize() * pagination.getCurrentPage();
            paginationSql = paginationSql.replaceAll(":_limit", String.valueOf(db2Limit));
        }
        return paginationSql;
    }

    /**
     * 装配分页信息
     *
     * @param template 模板
     * @param sql SQL语句
     * @param paramMap 查询参数
     * @param pagination 分页
     * @param dbType 数据源类型
     */
    private void configurePagination(NamedParameterJdbcTemplate template, String sql, Map<String, Object> paramMap,
                                     Pagination pagination, String dbType) {
        if (pagination.getTotalRows() == 0 || pagination.getTotalRows() == -1) {
            /** 获取数据总数 */
            int totalRows = template.queryForObject(generateCountSql(sql, dbType), DalUtils.mapIfNull(paramMap),
                    Integer.class);
            pagination.setTotalRows(totalRows);
        }
    }

    /**
     * 查询指定SQL的总记录数
     *
     * @param sql SQL语句
     * @param dbType 数据源类型
     * @return 统计SQL串
     */
    private String generateCountSql(String sql, String dbType) {
        Dialect dialect = dialectFactory.getDBDialect(dbType);
        if (dialect == null) {
            throw new DalException("error.dal.005", "DB Dialect is not find.");
        }
        return dialect.getCountString(sql);
    }
}
